package com.ht.api.init;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import org.reflections.Reflections;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

import com.ht.api.bigtask.BigTaskExecutor;
import com.ht.api.cache.impl.SysTenantCache;
import com.ht.api.constant.CacheConstants;
import com.ht.api.db.Db;
import com.ht.api.db.DbList;
import com.ht.api.db.Entity;
import com.ht.api.db.annotate.HtFunction;
import com.ht.api.db.bean.Column;
import com.ht.api.db.bean.Table;
import com.ht.api.db.cache.Cache;
import com.ht.api.db.remote.ApiConfig;
import com.ht.api.db.remote.RateLimit;
import com.ht.api.db.request.HandleRequestHelper;
import com.ht.api.db.task.HtTaskExecutor;
import com.ht.api.db.task.TaskGroup;
import com.ht.api.db.task.TaskInfo;
import com.ht.api.db.util.FunctionUtil;
import com.ht.api.db.util.RedisUtil;

import cn.hutool.json.JSONUtil;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;

/**
 * 项目启动后要做的操作
 * @author asq
 * @createTime 2024年12月17日 10:47:07
 */
@Slf4j
@Component
public class ApplicationInitializer {
	@Autowired
	private ApiConfig apiConfig;
	@Autowired
	private List<Cache> cacheList;
    @Autowired
    private Environment env;
    @Resource
    private SysTenantCache sysTenantCache;
	
	// 使用单一的初始化方法，严格控制顺序
	@PostConstruct
	private void init() {
		// 是否是生产环境
		boolean isProd = env.matchesProfiles("prod");
		
		// 生产环境加载全部缓存
		if (isProd) {
			// 加载所有元数据到缓存
			loadMetaDataList();
			
			// 加载所有的专用接口到call表
			HandleRequestHelper.loadFunction();
			
			// 检查call表的key是否有重复
			checkCallAndReportUnique();
			
			// 加载所有需要缓存的数据
			loadCaches();
			
			// 加载所有接口频率限制规则到缓存中
			loadRateLimitList();
			
        	// 继续执行定时任务
        	continueTask();
        	
        	// 继续执行大任务
            BigTaskExecutor.continueBigTask();
        } else {
        	// 非生产环境，加载必须的数据源
        	sysTenantCache.load();
        }
	}

	/**
	 * 检查call表及report表是否存在重复的key
	 */
	private void checkCallAndReportUnique() {
		// 检查call表
		DbList list = Db.query("select `key` from `call` where sys_deleted=1 group by `key` having count(1)>1");
		if (!list.isEmpty()) {
			String keyString = list.stream().map(c -> c.getStr("key")).collect(Collectors.joining(","));
			log.error("[警告]call表存在重复key：{}", keyString);
		}
		
		// 检查report表
//		DbList listReport = Db.query("select `key` from `report` where sys_deleted=1 group by `key` having count(1)>1");
//		if (!listReport.isEmpty()) {
//			String keyString = listReport.stream().map(c -> c.getStr("key")).collect(Collectors.joining(","));
//			log.error("[警告]report表存在重复key：", keyString);
//		}
	}

	/**
	 * 加载所有需要缓存的数据到redis
	 */
	public void loadCaches() {
		// 遍历所有的cache接口实现类，调用其load方法，加载数据到redis
		cacheList.stream().forEach(Cache::load);
	}
	
	// 加载所有元数据到缓存
	public void loadMetaDataList() {
	    log.info("[init]加载所有元数据开始...");
	    try {
	        StringBuilder sb = new StringBuilder();
	        sb.append("select t.*,c.main_id c_main_id,c.code c_code,c.name c_name");
	        sb.append(",c.type c_type,c.fun c_fun,c.default_value c_default_value");
	        sb.append(",c.primary_key c_primary_key,c.dict_id c_dict_id,c.mapper_config c_mapper_config");
	        sb.append(" from db_table t inner join db_column c on t.code=c.table_code order by t.code");
	        DbList list = Db.query(sb.toString());
	        
	        // 按表代码分组
	        Map<String, List<Entity>> groupedData = list.stream()
	            .collect(Collectors.groupingBy(e -> e.getStr("code")));
	            
	        // 处理每个分组，构造 Table 对象
	        Map<String, Table> tables = groupedData.entrySet().stream()
	            .collect(Collectors.toMap(
	                Map.Entry::getKey,
	                entry -> {
	                    List<Entity> records = entry.getValue();
	                    Entity tableRecord = records.get(0); // 取第一条记录的表信息
	                    
	                    // 构造 Table 对象
	                    Table table = new Table();
	                    table.setCode(tableRecord.getStr("code"))
	                        .setMainId(tableRecord.getLong("main_id"))
	                        .setName(tableRecord.getStr("name"))
	                        .setTenant(tableRecord.getBool("tenant"))
	                        .setAlias(tableRecord.getStr("alias"))
		                    .setDelConfig(tableRecord.getStr("del_config"));
	                    
	                    // 处理列信息
	                    records.forEach(record -> {
	                        Column column = new Column();
	                        column.setId(record.getStr("c_main_id"));
	                        column.setTable(record.getStr("code"));
	                        column.setCode(record.getStr("c_code"));
	                        column.setName(record.getStr("c_name"));
	                        column.setType(record.getStr("c_type"));
	                        column.setFun(record.getStr("c_fun"));
	                        column.setDefaultValue(record.getStr("c_default_value"));
	                        column.setPrimaryKey(record.getInt("c_primary_key"));
	                        column.setDict(record.getLong("c_dict_id"));
	                        column.setMapperConfig(record.getStr("c_mapper_config"));
	                        
	                        table.getColumns().add(column);
	                    });
	                    
	                    // 设置主键列
	                    table.getPrimaryKeys().addAll(
	                        records.stream()
	                            .filter(r -> r.getObj("c_primary_key") != null && r.getInt("c_primary_key") !=null && r.getInt("c_primary_key") > 0)
	                            .sorted((r1, r2) -> r1.getInt("c_primary_key") - r2.getInt("c_primary_key"))
	                            .map(r -> r.getStr("c_code"))
	                            .collect(Collectors.toList())
	                    );
	                    
	                    return table;
	                }
	            ));
	            
	        // 批量写入 Redis
	        if (!tables.isEmpty()) {
	            Map<String, Object> redisData = tables.entrySet().stream()
	                .collect(Collectors.toMap(
	                    e -> CacheConstants.KEY_TABLE + e.getKey(),
	                    Map.Entry::getValue
	                ));
	            
	            RedisUtil.mset(redisData);
	            log.info("[init]成功加载{}张表的元数据到缓存", tables.size());
	        }
	        
	    } catch (Exception e) {
	        log.error("[init]加载所有元数据失败: {}", e.getMessage(), e);
	    }
	}

	/**
     * Remote:加载所有接口频率限制规则到缓存中
     */
	private void loadRateLimitList() {
        try {
        	// 从数据库加载所有的接口频率限制规则
        	DbList list = Db.query("select * from rate_limit order by url,time");
        	log.info("[ApiConfig]加载三方接口访问频率规则：共{}条", list.size());
        	
        	// 全部写入缓存
        	List<RateLimit> allRateList = new ArrayList<>();
        	list.stream().forEach(entity -> {
        		// 构造对象
        		RateLimit rate = new RateLimit();
        		rate.setUrl(entity.getStr("url"));
        		rate.setTime(entity.getLong("time"));
        		rate.setMax(entity.getInt("max"));
        		rate.setName(entity.getStr("name"));
        		
        		// 更新缓存
        		if (RedisUtil.exists(rate.getCacheKey())) {
        			String rateJson = RedisUtil.get(rate.getCacheKey());
        			rate = JSONUtil.toBean(rateJson, RateLimit.class);
        			rate.setMax(entity.getInt("max")); // 更新最大访问频率，因为第三方平台可能有变化
            		rate.setName(entity.getStr("name"));
            		apiConfig.updateCache(rate);
        			log.info("[ApiConfig]缓存已有规则，更新：{}", JSONUtil.toJsonStr(rate));
        		} else {
        			String jsonStr = JSONUtil.toJsonStr(rate);
        			RedisUtil.set(rate.getCacheKey(), jsonStr, rate.getTime(), TimeUnit.SECONDS);
        			log.info("[ApiConfig]缓存没有本规则，写入：{}，过期时间：{}", jsonStr, rate.getTime());
        		}
        		
        		// 同时保存到allRateList，下面代码要用
        		allRateList.add(rate);
        	});
        	
        	// 相同url写入rateMap中的同一个key，成为一组
        	for (int i = 0; i < allRateList.size(); i++) {
        		RateLimit rate = allRateList.get(i);
        		List<RateLimit> subRateLimitList = null;
        		if (ApiConfig.rateMap.containsKey(rate.getUrl())) {
        			subRateLimitList = ApiConfig.rateMap.get(rate.getUrl());
        		} else {
        			subRateLimitList = new ArrayList<>();
        			ApiConfig.rateMap.put(rate.getUrl(), subRateLimitList);
        		}
        		subRateLimitList.add(rate);
			}
        } catch (Exception e) {
			log.error("[init]系统启动，加载接口频率限制规则出现异常：{}", e.getMessage(), e);
        }
    }

	/**
	 * Task:继续执行定时任务
	 */
	private void continueTask() {
		try {
			log.info("[task]系统启动，将缓存中的定时任务写入数据库...");
			Map<Object, Object> mapTaskGroup = RedisUtil.entries(HtTaskExecutor.KEY_GROUP);
			log.info("[task]从缓存中找到{}个taskGroup", mapTaskGroup.size());
			Map<Object, Object> mapTask = RedisUtil.entries(HtTaskExecutor.KEY_TASK);
			log.info("[task]从缓存中找到{}个task", mapTask.size());
			List<Entity> taskList = new ArrayList<>();
			mapTaskGroup.values().stream().forEach(t -> {
				Entity taskGroup = (Entity)t;
				taskGroup.setTable(taskGroup.getStr("table"));
				taskList.add(taskGroup);
			});
			mapTask.values().stream().forEach(t -> {
				Entity task = (Entity)t;
				task.setTable(task.getStr("table"));
				taskList.add(task);
			});
			if (!taskList.isEmpty()) {
				Db.insertBatchWithStream(taskList);
			}
			RedisUtil.delete(HtTaskExecutor.KEY_GROUP);
			RedisUtil.delete(HtTaskExecutor.KEY_TASK);
			
			log.info("[task]系统启动，继续执行任务...");
			String sql = "select * from db_task where status='RUNNING'";
			DbList list = Db.query(sql);
			list.stream().forEach(o -> {
				TaskInfo taskInfo = TaskInfo.load(o);
				
				// 加载任务组
				if (taskInfo.getGroupId() != null && !HtTaskExecutor.groups.containsKey(taskInfo.getGroupId())) {
					Entity entity = Db.findOne("select * from db_task_group where main_id=?", taskInfo.getGroupId());
					TaskGroup taskGroup = TaskGroup.load(entity);
					HtTaskExecutor.groups.put(taskInfo.getGroupId(), taskGroup);
				}
				
				log.info("[task]继续任务,taskId:{}, json:{}", taskInfo.getTaskId(), o.toString());
				HtTaskExecutor.startTask(taskInfo);
			});
		} catch (Exception e) {
			log.error("[init]系统启动，加载Task任务出现异常：{}", e.getMessage(), e);
		}
	}
	
	//加载全部有@HtFunction注解的接口，将所有实现类加载到{@link FunctionUtil}工具类中
	@PostConstruct
	private void loadHtFunctionList() {
		log.info("[init]函数扫描加载开始...");
		int count = 0;
        try {
            // 创建Reflections对象
            Reflections reflections = new Reflections("com.ht.api");
            
            // 获取包下面的所有有@HtFunction注解的类
            Set<Class<?>> interfaces = reflections.getTypesAnnotatedWith(HtFunction.class);
            for (Class<?> interfaceClass : interfaces) {
                // 验证是否是接口
                if (!interfaceClass.isInterface()) {
                    continue;
                }
                
                // 获取该接口的所有实现类
                Set<?> implementations = reflections.getSubTypesOf(interfaceClass);
                
                // 遍历实现类并实例化
                for (Object impl : implementations) {
                	Class<?> implClass = (Class<?>) impl;
                    try {
                        // 判断是否是抽象类
                        if (Modifier.isAbstract(implClass.getModifiers())) {
                            continue;  // 跳过抽象类
                        }

                        count ++;
                        FunctionUtil.setFunction(implClass.getSimpleName(), implClass.getDeclaredConstructor().newInstance());
                    } catch (Exception e) {
                        log.error("[init]加载函数失败: {}, 错误: {}", implClass.getName(), e.getMessage(), e);
                    }
                }
            }
                        
        } catch (Exception e) {
            log.error("[init]函数扫描加载失败: {}", e.getMessage(), e);
        }
		log.info("[init]函数扫描加载结束，共加载{}个函数。", count);
    }
}
